package com.tencent.qplus.paddata.stat;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import com.tencent.qplus.paddata.db.DataSourceManager;
import com.tencent.qplus.paddata.util.ConfigUtil;
import com.tencent.qplus.paddata.util.JdbcUtils;


/**
 * 解析JavaCGI的日志,主要解析version上报信息串
 * 解析出登录的设备信息、版本信息
 * 
 * @author shiangzhang
 *
 * 2012-1-4
 */
public class ParseLogFileJob extends Job{
	
	private static Map<String,Long> deviceNameIdMap = new ConcurrentHashMap<String,Long>();
	
	private static Pattern logLinePattern = Pattern.compile("^\\[\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3}\\]");
	
	private static Set<Long> hasScreenSizeDeviceSet = new HashSet<Long>();
	private static DecimalFormat screenSizeDf = new DecimalFormat("#.#");
	
	private Logger logger = Logger.getLogger(ParseLogFileJob.class);
	
	private String serverKey = null;
	private static String[] serverKeys = null;
	
	private Map<String,LogData> logDatas = new HashMap<String,LogData>();
	
	@Override
	public void init() {
		ResultSet rs = null;
		Statement st = null;
		Connection deviceIdConn = null;
//		if(serverKey==null){
//			serverKey = ConfigUtil.getSeverKey();
//		}
		serverKeys = ConfigUtil.getString("total_logservers").split(";");
		
		
		try {
			deviceIdConn = DataSourceManager.getInstance().getDataSource("paddatadb").getConnection();
			String sql = "select id,deviceName from devices";
			st = deviceIdConn.createStatement();
			rs = st.executeQuery(sql);
			while(rs.next()){
				Long id = rs.getLong(1);
				String name = rs.getString(2);
				deviceNameIdMap.put(name, id);
			}
			
			sql = "select id from devices where screenSize<>''";
			st = deviceIdConn.createStatement();
			rs = st.executeQuery(sql);
			while(rs.next()){
				Long id = rs.getLong(1);
				hasScreenSizeDeviceSet.add(id);
			}
			
		} catch (SQLException e) {
			logger.error(e);
		}finally{
			JdbcUtils.release(rs);
			JdbcUtils.release(st);
			JdbcUtils.release(deviceIdConn);
		}
	}
	
	
	private File[] getLogFiles(final String statDate){
		String logFilePath = ConfigUtil.getString("logfile_path_"+this.serverKey);
		File path = new File(logFilePath);
		FileFilter filter = new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				if(pathname.isFile()){
					String fileName = pathname.getName();
					if(fileName.startsWith("msg-") && fileName.endsWith(".log."+statDate))
						return true;
				}
				return false;
			}
		};
		return path.listFiles(filter);
	}
	
	private void parseFile(String statDate,File logFile){
	    String tempLine = null;
	    LogData data = null;
		try {
			BufferedReader br = new BufferedReader(new FileReader(logFile));
			String line=null;
			int lineNum = 0;
			int versionLineNum = 0;
			
			int nextLineNum = 0;
			while((line = br.readLine())!=null){
				try{
					lineNum++;
					if(line.indexOf("version=")>0){
						tempLine=line;
						nextLineNum=0;
					}else{
						nextLineNum++;
						if(logLinePattern.matcher(line).find()){
							continue;
						}else{
							if(nextLineNum != 1)continue;
							tempLine += line;
							
						}
					}
					tempLine = tempLine.replaceAll("'","\\\\'");
				    String loginDate = tempLine.substring(1,11);
				    int begin = tempLine.indexOf("=")+1;
				    String versionStr = tempLine.substring(begin);
				    String[] ss = versionStr.split("\\|");
				    if("100".equals(ss[1]))continue;
				    if(ss.length==11){
					    data = new LogData();
					    data.loginDate = loginDate;
					    data.version = ss[0];
					    data.uin = ss[1];
					    data.loginType = ss[2];
			    		data.phoneSys = ss[3];
			    		data.dpi = ss[4];
			    		data.resolution = ss[5];
			    		data.hasSim = ss[6];
			    		data.romVersion = ss[7];
			    		data.ramSize = ss[8];
					    data.cpu = ss[9];
					    data.deviceName = ss[10];
					    data.deviceId = getDeviceId(data);
					    versionLineNum++;
					    logDatas.put(data.uin+"#"+data.deviceId+"#"+data.loginDate, data);
				    }else if(ss.length==14 || ss.length==16){
			    	 	data = new LogData();
					    data.loginDate = loginDate;
					    data.version = ss[0];
					    data.uin = ss[1];
					    data.loginType = ss[2];
			    		data.phoneSys = ss[3];
			    		data.dpi = ss[4];
			    		data.xDpi = ss[5];
			    		data.yDpi = ss[6];
			    		data.density = ss[7];
			    		data.resolution = ss[8];
			    		data.hasSim = ss[9];
			    		data.romVersion = ss[10];
			    		data.ramSize = ss[11];
					    data.cpu = ss[12];
					    data.deviceName = ss[13];
					    data.deviceId = getDeviceId(data);
					    versionLineNum++;
					    logDatas.put(data.uin+"#"+data.deviceId+"#"+data.loginDate, data);
				    }
				}catch (SQLException e) {
					logger.error(e);
					logger.info(tempLine);
					logger.info(data.toString());
				}
			}
			logger.info("Fils:<<"+logFile.getName()+">>totalLine:"+lineNum+"versionLine:"+versionLineNum);
			
		} catch (FileNotFoundException e) {
			logger.error(e);
		} catch (IOException e) {
			logger.error(e);
		} 
	}
	
	public void insertIntoDb(){
		ThreadPoolExecutor pool = new ThreadPoolExecutor(8, 10, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
		Set<Entry<String, LogData>> entrySet = logDatas.entrySet();
		for(int i=0;i<100;i++){
			DbOptThread t = new DbOptThread(i);
			for(Entry<String, LogData> entry:entrySet){
				 int tableIndex = 0;
				    try{
				    	tableIndex = Integer.valueOf(entry.getValue().uin)%100;
				    }catch(Exception e){
				    	continue;
				    }
				if(tableIndex==i){
					try {
						t.add(entry.getValue());
					} catch (Exception e) {
						logger.error(e);
					}
				}
			}
			pool.execute(t);
		}
		pool.shutdown();
		
		logDatas.clear();
		//同步等到所有线程都执行完
		while(!pool.isTerminated()){
			try {
				Thread.sleep(60000);
				logger.info("wait for pool terminated!!");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	//|196.64516|191.69812
	public static String calScreenSize(String xdpi,String ydpi,String resolution){
		try{
			String[] xy = resolution.split("\\*");
			int x = Integer.valueOf(xy[0]);
			int y = Integer.valueOf(xy[1]);
			float xd = Float.valueOf(xdpi);
			float yd = Float.valueOf(ydpi);
			String size = String.valueOf(screenSizeDf.format(Math.sqrt((x/xd)*(x/xd)+(y/yd)*(y/yd))));
			return size;
		}catch(Exception e){
			e.printStackTrace();
		}
		return "";
	}
	private Long getDeviceId(LogData data) throws SQLException {
		
		String sql= null;
	    if(deviceNameIdMap.containsKey(data.deviceName)){
	    	Long id = deviceNameIdMap.get(data.deviceName);
	    	if(data.version.equals("14") && !hasScreenSizeDeviceSet.contains(id)){
	    		String screenSize = calScreenSize(data.xDpi,data.yDpi,data.resolution);
	    		if(screenSize!=null && screenSize.length()>10)screenSize="";
	    		sql = "update devices set xdpi='"+data.xDpi+"',ydpi='"+data.yDpi+"',density='"+data.density+"',screenSize='"+screenSize+"' where id="+id;
	    		Statement st = null;
	    		Connection deviceIdConn = null;
	    		try {
	    			deviceIdConn = DataSourceManager.getInstance().getDataSource("paddatadb").getConnection();
					st = deviceIdConn.createStatement();
					st.execute(sql);
					hasScreenSizeDeviceSet.add(id);
				} catch (SQLException e) {
					JdbcUtils.release(deviceIdConn);
					throw e;
				}finally{
					JdbcUtils.release(st);
					JdbcUtils.release(deviceIdConn);
				}
	    	}
	        return id;
	    }
	    Connection deviceIdConn = null;
	    Statement st = null;
	    ResultSet rs = null;
        Long deviceId = 0L;
        try {
        	deviceIdConn = DataSourceManager.getInstance().getDataSource("paddatadb").getConnection();
			st = deviceIdConn.createStatement();
	        sql = "select id from devices where deviceName='"+data.deviceName+"'";
	        rs = st.executeQuery(sql);
	        if(rs.next()){
	        	deviceId = rs.getLong(1);
	        	deviceNameIdMap.put(data.deviceName, deviceId);
	        	rs.close();
	        }else{
	        	String screenSize = "";
	        	if(data.version.compareTo("14")>=0)
	        		screenSize = calScreenSize(data.xDpi,data.yDpi,data.resolution);
		         sql = "INSERT INTO devices (deviceName,resolution,screenSize,dpi,xdpi,ydpi,density,ramSize,cpu)" +
		         		" VALUES('"+data.deviceName+"','"+data.resolution+"','"+screenSize+"','"+data.dpi+"','"+data.xDpi+"','"+data.yDpi+"','"+data.density+"','"+data.ramSize+"','"+data.cpu+"');";
		         st.execute(sql, Statement.RETURN_GENERATED_KEYS);
		         rs = st.getGeneratedKeys();
		         if(rs.next()){
		         	deviceId = rs.getLong(1);
		         	deviceNameIdMap.put(data.deviceName, deviceId);
		         	if(data.version.compareTo("14")>=0)
		         		hasScreenSizeDeviceSet.add(deviceId);
		         	rs.close();
		         }
	        }
        } catch (SQLException e) {
        	logger.error(e);
        	throw e;
		}finally{
			JdbcUtils.release(rs);
			JdbcUtils.release(st);
			JdbcUtils.release(deviceIdConn);
		}
	    return deviceId;
	}
	
	private void updateStatRecorde(){
		Statement st = null;
		Connection conn = null;
		String sql = "INSERT INTO logparsedone(statdate,serverkey,updateTime) VALUES('"+statDate+"','" + this.serverKey + "',NOW()) ON DUPLICATE KEY UPDATE updateTime=NOW();";
		try {
			conn = DataSourceManager.getInstance().getDataSource("paddatadb").getConnection();
			st = conn.createStatement();
			st.execute(sql);
		} catch (SQLException e) {
			logger.error(e);
		}finally{
			JdbcUtils.release(st);
			JdbcUtils.release(conn);
		}
	}
	
//version=12|270766733|0|1|240|854*480|8|5|477.28906MB| ARMv7 Processor rev 2 (v7l)|motorola MotoA953
	@Override
	public void run() {
		init();
		for(String serverKey:serverKeys){
			this.serverKey = serverKey;
			File[] logFiles = getLogFiles(statDate);
			for(File logFile:logFiles){
				parseFile(statDate, logFile);
			}
			insertIntoDb();
			updateStatRecorde();
		}
	}

	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//196.64516 | 191.69812
//		String s = calScreenSize("196.64516","191.69812","480*800");
//		System.out.println(s);
		ParseLogFileJob job = new ParseLogFileJob();
		job.stat(args);
	}

	@Override
	public void stat(String[] args) {
		if(args!=null && args.length==1 && dateUtils.isDateStr(args[0])){
			this.statDate = args[0];
		}
		logger.info("start stat..................: "+statDate);
		run();
		logger.info("stat done..................: "+statDate);
	}
}
